package org.happen.android.core.data.image;

import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.DisplayMetrics;
import android.util.Log;

/**
 * <pre>
 * int SKY_MODE = ImageCacheManager.MODE_LEAST_RECENTLY_USED;
 * String SKY_TAG = &quot;sky&quot;;
 * ImageCacheManager mImageCacheManager;
 * mImageCacheManager = ImageCacheManager.getCacheManager(cxt, Constant.SKY_MODE, Constant.SKY_TAG);
 * 
 * mImageCacheManager.getFromCache(favorable.thumb, new ImgCacheCallback(favorable.thumb, holder.imgView_favorabledetail));
 * 
 * class ImgCacheCallback implements ICallback
 * {
 * 	private ImageView iv;
 * 
 * 	public ImgCacheCallback(String url, ImageView imageView)
 * 	{
 * 		iv = imageView;
 * 	}
 * 
 * 	&#064;Override
 * 	public void callback(String url, SoftReference&lt;Bitmap&gt; bmp)
 * 	{
 * 		if (iv != null &amp;&amp; bmp != null &amp;&amp; bmp.get() != null)
 * 		{
 * 			iv.setImageBitmap(bmp.get());
 * 		}
 * 	}
 * }
 * </pre>
 */
public class ImageCacheManager
{
	private HashMap<String, SoftReference<Bitmap>> mImageCache;
	private static ImageCacheManager mCacheManager;
	private ImageDbCacheManager mDbCacheManager;
	private Context mContext;

	private static final int LOAD_IMAGE_SUCCESS = 1;
	private static final int LOAD_IMAGE_FAIL = -1;

	private static final String TAG = "ImageCacheManager";

	public static final int MODE_LEAST_RECENTLY_USED = 0;
	public static final int MODE_FIXED_TIMED_USED = 1;
	public static final int MODE_FIXED_MEMORY_USED = 2;
	public static final int MODE_NO_CACHE_USED = 3;

	private static int WINDOW_WIDTH = 0;
	private static int WINDOW_HEIGHT = 0;

	//	public static final int FLAG_DOWNLOAD_METHOD_HTTP = 101;
	//	public static final int FLAG_DOWNLOAD_METHOD_FTP = 102;

	private ImageCacheManager(Context context, int mode, String tag)
	{
		mContext = context;
		mDbCacheManager = ImageDbCacheManager.getImageCacheService(context, mode, tag);
		if (mImageCache == null)
		{
			mImageCache = new HashMap<String, SoftReference<Bitmap>>();
		}

		DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
		WINDOW_WIDTH = dm.widthPixels;
		WINDOW_HEIGHT = dm.heightPixels;
	}

	/**
	 * get ImageCacheManager create table name = mode + tag
	 * 
	 * @ReqParam context
	 * @ReqParam mode the mode is
	 * MODE_LEAST_RECENTLY_USED,MODE_FIXED_TIMED_USED,
	 * MODE_FIXED_MEMORY_USED,MODE_NO_CACHE_USED
	 * @ReqParam tag
	 * @return
	 */

	public static synchronized ImageCacheManager getCacheManager(Context context, int mode, String tag)
	{
		if (mCacheManager == null)
		{
			mCacheManager = new ImageCacheManager(context, mode, tag);
		}
		return mCacheManager;
	}

	/**
	 * 
	 * @ReqParam urlStr
	 * @ReqParam callback
	 */
	public void getFromCache(final String urlStr, final ICallback callback)
	{
		Log.i(TAG, "getFromCache(), url = " + urlStr);
		boolean willDownload = true;
		if (mImageCache.containsKey(urlStr))
		{
			SoftReference<Bitmap> value = mImageCache.get(urlStr);
			if (value != null && value.get() != null)
			{
				willDownload = false;
				callback.callback(urlStr, value);
			} else
			{
				willDownload = true;
				mImageCache.remove(urlStr);
			}
		} else if (!isHttp(urlStr))
		{ // file io ?
			try
			{
				willDownload = false;
				final Handler handler = new Handler() {
					public void handleMessage(Message msg)
					{
						SoftReference<Bitmap> value = mImageCache.get(urlStr);
						if (value != null && value.get() != null)
						{
							callback.callback(urlStr, value);
						} else
						{
							mImageCache.remove(urlStr);
							callback.callback(urlStr, null);
						}
					}
				};
				new Thread(new Runnable() {
					public void run()
					{
						Bitmap bmp = readImage(urlStr);
						if (bmp != null)
						{
							mImageCache.put(urlStr, new SoftReference<Bitmap>(bmp));
						}
						Message msg = handler.obtainMessage();
						handler.sendMessage(msg);

					}
				}).start();
			} catch (Exception ex)
			{
				Log.e(TAG, "exception ", ex);
				mImageCache.remove(urlStr);
				callback.callback(urlStr, null);
			} catch (OutOfMemoryError error)
			{
				Log.e(TAG, "************ XXX. outofmemoryerror *********", error);
				recycle();
				callback.callback(urlStr, null);
			}
		}

		if (!willDownload)
		{
			return;
		} else
		{
			final Handler handler = new Handler() {
				public void handleMessage(Message msg)
				{
					switch (msg.what)
					{
						case LOAD_IMAGE_SUCCESS:
							callback.callback(urlStr, mImageCache.get(urlStr));
							break;
						case LOAD_IMAGE_FAIL:
							callback.callback(urlStr, mImageCache.get(urlStr));
							break;
						default:
							callback.callback(urlStr, null);
							break;
					}
				}
			};

			Thread thread = new Thread(new Runnable() {
				public void run()
				{
					Message msg = handler.obtainMessage(LOAD_IMAGE_FAIL);
					try
					{
						URL url = new URL(urlStr);
						CacheInfo cacheInfo = mDbCacheManager.downloadImage(url);
						if (cacheInfo != null)
						{
							Bitmap bmp = null;
							msg.what = LOAD_IMAGE_SUCCESS;
							bmp = readImage(cacheInfo.getFileName());
							if (bmp != null)
							{
								mImageCache.put(urlStr, new SoftReference<Bitmap>(bmp));
							} else
							{
								mImageCache.remove(urlStr);
							}
						} else
						{
							msg.what = LOAD_IMAGE_FAIL;
						}

					} catch (MalformedURLException e)
					{
						Log.e(TAG, "InValid url = " + urlStr, e);
					}
					handler.sendMessage(msg);
				}
			});
			thread.start();
		}
	}

	private Bitmap readImage(String filePathName)
	{
		Bitmap bmp = null;
		try
		{
			BufferedInputStream is = new BufferedInputStream(FilesUtils.getSaveInputStream(mContext, filePathName));
			BitmapFactory.Options bounds = new BitmapFactory.Options();
			bounds.inPreferredConfig = Bitmap.Config.RGB_565;
			bmp = BitmapFactory.decodeStream(is, null, bounds);
			is.close();
		} catch (FileNotFoundException e)
		{
			Log.e(TAG, "************ exception *********", e);
		} catch (IOException e)
		{
			Log.e(TAG, "************ exception *********", e);
		} catch (OutOfMemoryError err)
		{
			Log.e(TAG, "************ 1. OutOfMemory *********", err);
			recycle();
			try
			{
				BufferedInputStream is = new BufferedInputStream(FilesUtils.getSaveInputStream(mContext, filePathName));
				BitmapFactory.Options bounds = new BitmapFactory.Options();
				bounds.inPreferredConfig = Bitmap.Config.RGB_565;
				bounds.inJustDecodeBounds = false;
				//         	bounds.inSampleSize = computeSampleSize(filePathName);
				bmp = BitmapFactory.decodeStream(is, null, bounds);
				is.close();
			} catch (FileNotFoundException e)
			{
				Log.e(TAG, "************ exception *********", e);
			} catch (Exception e)
			{
				Log.e(TAG, "************ exception *********", e);
			} catch (OutOfMemoryError err2)
			{
				Log.e(TAG, "************ 2. OutOfMemory *********", err2);
				recycle();
				try
				{
					BufferedInputStream is = new BufferedInputStream(FilesUtils.getSaveInputStream(mContext, filePathName));
					BitmapFactory.Options bounds = new BitmapFactory.Options();
					bounds.inPreferredConfig = Bitmap.Config.RGB_565;
					bounds.inJustDecodeBounds = false;
					bounds.inSampleSize = computeSampleSize(filePathName);
					bmp = BitmapFactory.decodeStream(is, null, bounds);
					is.close();
				} catch (FileNotFoundException e)
				{
					Log.e(TAG, "************ exception *********", e);
				} catch (Exception e)
				{
					Log.e(TAG, "************ exception *********", e);
				} catch (OutOfMemoryError err3)
				{
					Log.e(TAG, "************ 3. OutOfMemory *********", err2);
				}
			}
		}

		return bmp;
	}

	private int computeSampleSize(String filePathName)
	{
		int sampleSize = 1;
		BitmapFactory.Options opts = new BitmapFactory.Options();
		opts.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(filePathName, opts);
		int width = opts.outWidth;
		int height = opts.outHeight;
		int scale1 = width / WINDOW_WIDTH;
		int scale2 = height / WINDOW_HEIGHT;
		Log.i(TAG, "%%%%%%  outWidth = " + width + ",outHeight = " + height + ", targetWidth = " + WINDOW_WIDTH + //
				", targetHeight = " + WINDOW_HEIGHT + ", scale1 = " + scale1 + ", scale2 = " + scale2);
		int sam = Math.min(scale1, scale2);
		if (sam > 1)
		{
			sampleSize = sam;
		}
		Log.i(TAG, "%%%%%%%%%%% filename = " + filePathName + ", sampleSize = " + sampleSize);
		return sampleSize;
	}

	private boolean isHttp(String path)
	{
		if (path != null)
		{
			if (path.startsWith("http") || path.startsWith("https"))
			{
				return true;
			}
		}
		return false;
	}

	/**
	 * 
	 * @return the image cache
	 */
	public HashMap<String, SoftReference<Bitmap>> getCache()
	{
		return this.mImageCache;
	}

	/**
	 * set the image cache
	 * 
	 * @ReqParam cache
	 */
	public void setCache(HashMap<String, SoftReference<Bitmap>> cache)
	{
		this.mImageCache = cache;
	}

	/**
	 * put a pair of <String,SoftReference<Bitmap> into the cache
	 * 
	 * @ReqParam url
	 * @ReqParam bmp
	 */
	public void put(String url, Bitmap bmp)
	{
		if (mImageCache == null)
		{
			mImageCache = new HashMap<String, SoftReference<Bitmap>>();
		}
		if (mImageCache.containsKey(url))
		{
			mImageCache.remove(url);
		}
		SoftReference<Bitmap> value = new SoftReference<Bitmap>(bmp);
		mImageCache.put(url, value);
	}

	public boolean ifContains(String url)
	{
		if (mImageCache == null)
		{
			mImageCache = new HashMap<String, SoftReference<Bitmap>>();
		}
		return mImageCache.containsKey(url);
	}

	public Bitmap getFromCache(String key)
	{
		if (mImageCache == null)
		{
			mImageCache = new HashMap<String, SoftReference<Bitmap>>();
			return null;
		}
		if (mImageCache.get(key) != null)
		{
			return mImageCache.get(key).get();
		}
		return null;
	}

	/**
	 * 释放已存储的缓存
	 */
	public void recycle()
	{
		if (mImageCache == null || mImageCache.isEmpty())
		{
			return;
		}
		try
		{
			Set<String> keys = mImageCache.keySet();
			Iterator<String> iterator = keys.iterator();
			while (iterator.hasNext())
			{
				String key = iterator.next();
				SoftReference<Bitmap> soft = mImageCache.get(key);
				if (soft != null && soft.get() != null)
				{
					mImageCache.put(key, null);
					soft.get().recycle();
					System.gc();
				}
			}

			mImageCache.clear();
		} catch (Exception ex)
		{
			Log.i(TAG, "exception when recycle a bitmap", ex);
		}
	}

	public void clearCache()
	{
		recycle();
		DBClient dbClient = mDbCacheManager.getDbClient();
		dbClient.deleteAll(dbClient.getWritableDatabase());
	}

	public interface ICallback
	{
		public void callback(String url, SoftReference<Bitmap> bmp);
	}
}